﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace _4Gewinnt
{

    public class Node
    {

        // The column the node put its coin
        public int column { get; set; }

        //sbr: Muss dieser Knoten für den "großen" Minimax berücksichtigt werden (er muss nicht berücksichtigt werden, falls wir eh nicht in diese Spalte legen dürfen)
        public bool consider { get; set; }

        private GameColor MAX;
        private GameColor MIN;

        //sbr: Gewichtung für die Anzahl der Reihen der Steine in einer Reihe
        private int[] MAX_ROW = { 0, 1, 10, 500, 10000, 10000, 10000 };
        private int[] MIN_ROW = { 0, -1, -10, -450, -10000, -10000, -10000 };

        /// <summary>
        /// Repräsentiert einen Knoten im Suchbaum.
        /// </summary>
        /// <param name="column">Spalte, in die MAX oder MIN gezogen hat</param>
        /// <param name="MAX">Wir</param>
        /// <param name="MIN">Der Gegner</param>
        /// <param name="consider">Muss der Knoten für die 3te Phase berücksichtigt werden</param>
        public Node(int column, GameColor MAX, GameColor MIN, bool consider)
        {
            this.column = column;
            this.MAX = MAX;
            this.MIN = MIN;
            this.consider = consider;
        }

        public Node(int column, GameColor MAX, GameColor MIN, bool consider, int[] MAX_ROW, int[] MIN_ROW)
            : this(column, MAX, MIN, consider)
        {
            this.MAX_ROW = MAX_ROW;
            this.MIN_ROW = MIN_ROW;
        }

        /// <summary>
        /// Returns the children (game states reachable from the current one) of the node
        /// </summary>
        /// <returns>The child nodes</returns>
        public List<Node> getChildren(RedoBoard state)
        {
            List<Node> children = new List<Node>(7);
            // Create the other game states, which are reachable from this node.
            // There are up to six other states, because there are 7 columns 
            for (int i = 0; i < 7; i++)
            {
                // Check if we can put a coin at column i
                if (state.isLegalColumn(i))
                {
                    children.Add(new Node(i, MAX, MIN, true, MAX_ROW, MIN_ROW));
                }
            }
            return children;
        }

        /// <summary>
        /// Indicates whether the node represents a terminal game state. That is a user finished the game.
        /// </summary>
        /// <param name="board">The game state</param>
        /// <returns></returns>
        public bool isTerminal(RedoBoard state)
        {
            return (state.hasWon(GameColor.Blue) || state.hasWon(GameColor.Red));
        }

        public Tuple<int, int> getTupleToPreventDoubleThreats(RedoBoard state)
        {
            //sbr: Suche hier nach doppelten Bedrohungen und liefer eine solche Spalte zurück, die diese verhindert. "Bewertung" der 2. Phase des MinMax-Algorithmus.
            bool preceding = false;
            //sbr: Zähle erstmal alle Bedrohungen durch MIN
            List<List<int>> opsThreats = state.countThreats(MIN);

            for (int c = 0; c < opsThreats.Count; c++)
            {
                //sbr: Wenn es eine Zeile gibt mit mehr als 1 Bedrohung...
                if (opsThreats.ElementAt(c).Count > 1)
                {
                    for (int j = 0; j < opsThreats.ElementAt(c).Count - 1; j++)
                    {
                        //sbr: ...prüfe, ob diese Bedrohungen in direkt aufeinander folgenden Zeilen sind
                        if (opsThreats.ElementAt(c).ElementAt(j) == (opsThreats.ElementAt(c).ElementAt(j + 1)) - 1)
                        {
                            preceding = true;
                        }
                    }
                }

                //sbr: Gibt es eine Zeile mit mindestens 2 aufeinander folgenden Bedrohungen, ermittle die Zeile, die diese Bedrohgungen hoffentlich beseitigt
                if (preceding)
                {
                    return state.getDoubleThreatKiller(opsThreats.ElementAt(c), c, MIN);
                }
            }

            //sbr: Gibt es keine doppelten Bedrohungen in irgendeiner Spalte, liefere -1 zurück, so dass "weiter oben" mit der dritten Phase des MinMax-Algos begonnen werden kann
            return new Tuple<int, int>(-1, -1);
        }

        /// <summary>
        /// sbr: Gibt eine Bewertung der Spielsituation zurück. Diese Methode wird für alle Blätter im MinMax-Algorithmus aufgerufen. 
        /// Die konkrete Bewertung erfolgt parametergesteuert und ist verschieden für die einzelnen Phasen des MinMax-Algorithmus:
        /// Für die 1. Phase (Steuerung durch Parameter initialDepth == 1 und killThreats == false): Situation: MIN ist dran, MAX hat ggfs. so gezogen, dass MIN hier schon gewinnt. Sollte dies auftreten, 
        /// gebe int.MinValue() zurück, so dass "weiter oben" im Baum dieser Knoten für die folgenden Phasen rausgeschmissen wird.
        /// Für die 2. Phase (Steuerung durch Parameter killThreats == true): Geben die Spalte zurück, mit der hoffentlich verhindert werden kann, dass der Gegner doppelte aufeinanderfolgende Bedrohungen erhält.
        /// Für die 3. Phase (Wenn durch die Parameter die ersten beiden Phasen nicht gemeint sind): Bewerte die Situation anhand der Anzahl an xer-Reihen und gewichte diese. Bewertung MAX xer-Reihen positiv, MIN xer-
        /// Reihen negativ.
        /// </summary>
        /// <param name="board">The game state</param>
        /// <returns></returns>
        public int getScore(RedoBoard state, int initialDepth, bool killThreats)
        {

            //sbr: Verhindere hier, dass der Gegner durch unseren Zug gewinnen kann. "Bewertung" der 1. Phase des MinMax-Algoritmus
            if (initialDepth == 1)
            {
                int[] opsNoOf1 = state.countNumberOfRows(MIN);

                if (opsNoOf1[4] > 0 || opsNoOf1[5] > 0 || opsNoOf1[6] > 0)
                {
                    return int.MinValue;
                }
                return 0;
            }

            //sbr: Hier startet die Bewertung für die 3. Phase des MinMax-Algorithmus, in der tatsächlich eine Bewertung der aktuellen Gesamt-Spielsituation vorgenommen wird.
            if (state.hasWon(MIN))
            {
                return -100000;
            }

            int totalScore = 0;
            int totalScoreMe = 0;
            int totalScoreOp = 0;

            //sbr: Zähle die Anzahl der ier-Reihen von Max und Min
            int[] noOf = state.countNumberOfRows(MAX);
            int[] opsNoOf = state.countNumberOfRows(MIN);

            //sbr: Gewichte die einzelnen Anzahlen an ier-Reihen
            for (int i = 0; i < 7; i++)
            {
                totalScoreMe += MAX_ROW[i] * noOf[i];
                totalScoreOp += MIN_ROW[i] * opsNoOf[i];
            }

            totalScore = totalScoreMe + totalScoreOp;

            //sbr: liefere die Bewertung zurück
            return totalScore;
        }
    }
}
